﻿#include <json.hpp>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <assert.h>

using namespace nlohmann;
namespace ns {
	struct person {
		std::string name;
		std::string address;
		int age;
	};

	void to_json(json& j, const person& p)
	{
		j = json{ {"name", p.name}, {"address", p.address}, {"age", p.age} };
	}

	bool operator==(const person& p1, const person& p2) {
		return p1.age == p2.age &&
			p1.name == p2.name &&
			p1.address == p2.address;
	}

	void from_json(const json& j, person& p) {
		j.at("name").get_to(p.name);
		j.at("address").get_to(p.address);
		j.at("age").get_to(p.age);
	}
}

void test001();
void test_STL_like_access();
void test_user_struct();
void test_cin();

int main()
{
	//test001();
	//test_STL_like_access();
	//test_user_struct();
	test_cin();
	return 0;
}

void test_cin()
{
	//std::ifstream i("file.json");
	std::ifstream  i("D:/Share/桥梁建模/建模/2019/汩水大桥/参考/标准构件参数表.json");
	json j;
	i >> j;

	std::cout << j.dump(4) << std::endl;

	std::ofstream o("D:/Temp/pretty.json");
	o << std::setw(4) << j << std::endl;
}

void test_user_struct(){
	ns::person p{ "Ned Flanders", "744 Evergreen Terrace", 60 };
	json j = p;

	std::cout << j << std::endl;
	// {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"}

	// conversion: json -> person
	auto p2 = j.get<ns::person>();

	// that's it
	assert(p == p2);
}

void test001()
{
	// create an empty structure (null)
	json j;

	// add a number that is stored as double (note the implicit conversion of j to an object)
	j["pi"] = 3.141;

	// add a Boolean that is stored as bool
	j["happy"] = true;

	// add a string that is stored as std::string
	j["name"] = "Niels";

	// add another null object by passing nullptr
	j["nothing"] = nullptr;

	// add an object inside the object
	j["answer"]["everything"] = 42;

	// add an array that is stored as std::vector (using an initializer list)
	j["list"] = { 1, 0, 2 };

	// add another object (using an initializer list of pairs)
	j["object"] = { {"currency", "USD"}, {"value", 42.99} };

	// instead, you could also write (which looks very similar to the JSON above)
	json j2 = {
	  {"pi", 3.141},
	  {"happy", true},
	  {"name", "Niels"},
	  {"nothing", nullptr},
	  {"answer", {
		{"everything", 42}
	  }},
	  {"list", {1, 0, 2}},
	  {"object", {
		{"currency", "USD"},
		{"value", 42.99}
	  }}
	};

	//std::cout << j2 << std::endl;

	json j3333333 = "{ \"happy\": true, \"pi\": 3.141 }"_json;

	// or even nicer with a raw string literal
	auto j44444444 = R"(
	  {
		"happy": true,
		"pi": 3.141
	  }
	)"_json;

	// parse explicitly
	auto j3 = json::parse("{ \"happy\": true, \"pi\": 3.141 }");

	std::cout << j.dump() << std::endl;
	std::cout << j.dump(4) << std::endl;

	auto& p = j["pi"];
	std::cout << p << std::endl;

	// store a string in a JSON value
	json j_string = "this is a string";

	// retrieve the string value
	auto cpp_string = j_string.get<std::string>();
	// retrieve the string value (alternative when an variable already exists)
	std::string cpp_string2;
	j_string.get_to(cpp_string2);

	// retrieve the serialized value (explicit JSON serialization)
	std::string serialized_string = j_string.dump();

	// output of original string
	std::cout << cpp_string << " == " << cpp_string2 << " == " << j_string.get<std::string>() << '\n';
	// output of serialized value
	std::cout << j_string << " == " << serialized_string << std::endl;

	std::vector<std::uint8_t> v = { 't', 'r', 'u', 'e' };
	j = json::parse(v.begin(), v.end());
	std::cout << j << std::endl;
}

void test_STL_like_access()
{
	// create an array using push_back
	json j;
	j.push_back("foo");
	j.push_back(1);
	j.push_back(true);

	// also use emplace_back
	j.emplace_back(1.78);

	// iterate the array
	for (json::iterator it = j.begin(); it != j.end(); ++it) {
		std::cout << *it << '\n';
	}

	// range-based for
	for (auto& element : j) {
		std::cout << element << '\n';
	}

	// getter/setter
	const auto tmp = j[0].get<std::string>();
	j[1] = 42;
	bool foo = j.at(2);

	// comparison
	j == "[\"foo\", 1, true]"_json;  // true

	// other stuff
	j.size();     // 3 entries
	j.empty();    // false
	j.type();     // json::value_t::array
	j.clear();    // the array is empty again

	// convenience type checkers
	j.is_null();
	j.is_boolean();
	j.is_number();
	j.is_object();
	j.is_array();
	j.is_string();

	// create an object
	json o;
	o["foo"] = 23;
	o["bar"] = false;
	o["baz"] = 3.141;

	// also use emplace
	o.emplace("weather", "sunny");

	// special iterator member functions for objects
	for (json::iterator it = o.begin(); it != o.end(); ++it) {
		std::cout << it.key() << " : " << it.value() << "\n";
	}

	// the same code as range for
	for (auto& el : o.items()) {
		std::cout << el.key() << " : " << el.value() << "\n";
	}

	// even easier with structured bindings (C++17)
	for (auto& [key, value] : o.items()) {
		std::cout << key << " : " << value << "\n";
	}

	// find an entry
	if (o.find("foo") != o.end()) {
		// there is an entry with key "foo"
	}

	// or simpler using count()
	int foo_present = o.count("foo"); // 1
	int fob_present = o.count("fob"); // 0

	// delete an entry
	o.erase("foo");
}